package cn.seaboot.flake.ibatis;

import cn.seaboot.commons.file.IOUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;
import java.io.InputStream;

/**
 * mybatis 配置信息读取工具
 * <p>
 * mybatis 不支持动态修改配置，源码上直接做了限制，详见: {@link XMLMapperBuilder#parse()}
 * 如果期望 mybatis 支持动态修改配置，修改 xml 内容后，new 一个新的 Configuration 即可。
 *
 * @author Mr.css
 * @version 2023-06-06 16:18
 */
public class MyBatisConfiguration {
    private final Logger logger = LoggerFactory.getLogger(MyBatisConfiguration.class);
    private Configuration configuration = new Configuration();

    /**
     * 用于加载 xml 配置文件
     */
    private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();

    /**
     * 清除所有配置信息
     */
    public void clean() {
        configuration = new Configuration();
    }

    /**
     * 获取 xml中的配置信息
     *
     * @param id namespace + id, e.g.: cn.seaboot.db.test.dao.MenuDao.selectById
     * @return {{@link MappedStatement}}
     */
    public MappedStatement getMappedStatement(String id) {
        return configuration.getMappedStatement(id);
    }

    /**
     * 获取xml中的配置信息
     *
     * @param id namespace + id, e.g.: cn.seaboot.db.test.dao.MenuDao.selectById
     * @return {{@link MappedStatement}}
     */
    public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
        return configuration.getMappedStatement(id, validateIncompleteStatements);
    }

    /**
     * 加载 xml 实体文件
     *
     * @param locations xml位置信息
     * @throws IOException 内容读取失败
     */
    public void resolve(String... locations) throws IOException {
        for (String location : locations) {
            Resource[] resources = this.getResources(location);
            for (Resource resource : resources) {
                logger.debug("resolve location: {}", resource.toString());
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(),
                        configuration, resource.toString(), configuration.getSqlFragments());
                xmlMapperBuilder.parse();
            }
        }
    }

    /**
     * 获取资源，
     * <p>
     * 就是 springboot 项目中 yaml 中写的那个，
     *
     * @param location 配置路径，e.g.: classpath*: *.xml
     * @return Resource
     */
    private Resource[] getResources(String location) throws IOException {
        return resourceResolver.getResources(location);
    }

    /**
     * 加载包含 xml 信息的输入流
     *
     * @param name 资源名称，资源名称相同的情况下，不会覆盖历史配置
     * @param is   包含 xml 信息的输入流
     */
    public void resolve(String name, InputStream is) {
        logger.debug("resolve resource: {}", name);
        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(is,
                configuration, name, configuration.getSqlFragments());
        xmlMapperBuilder.parse();
    }

    /**
     * 加载字符串格式的 xml 配置
     *
     * @param name    资源名称，资源名称相同的情况下，不会覆盖历史配置
     * @param content xml 字符串内容
     */
    public void resolve(String name, String content) {
        this.resolve(name, IOUtils.stringToInputStream(content));
    }
}